/***********************************************
 * Microchip DAC library
 * (c) 2012 Majenko Technologies
 *
 * This library is offered without any warranty as to
 * fitness for purpose, either explicitly or implicitly
 * implied.
 ***********************************************/

/*
 * Microchip DAC library.  This works with the MCP4822 chip and
 * similar chips that work with the same protocol.
 */

#include <Arduino.h>
#include <SPI.h>
#include "MCPDAC.h"

MCPDACClass MCPDAC;

void MCPDACClass::begin()
{
	this->begin(10);
}

void MCPDACClass::begin(uint8_t cspin)
{
	this->ldac = false;
	this->cspin = cspin;
	pinMode(this->cspin, OUTPUT);
	digitalWrite(this->cspin, HIGH);
	pinMode(sclk, OUTPUT);
	delayMicroseconds(10);
	pinMode(sdi, OUTPUT);
	delayMicroseconds(10);
}

void MCPDACClass::begin(uint8_t cspin, uint8_t sclk, uint8_t sdi, uint8_t ldacpin)
{
	this->ldacpin = ldacpin;
	this->sclk = sclk;
	this->sdi = sdi;
	this->begin(cspin);
	this->ldac = true;
	pinMode(this->ldacpin, OUTPUT);
	digitalWrite(this->ldacpin, HIGH);
}

void MCPDACClass::setGain(bool chan, bool gain)
{
	this->gain[chan] = gain;
}

void MCPDACClass::ShutDown(bool chan, bool sd) {
    this->shdn[chan] = sd;
}

void MCPDACClass::setVoltage(bool chan, uint16_t mv)
{
	// if (this->value[chan] != mv)
	{
		this->value[chan] = mv;
		this->updateRegister(chan);
		this->action = true;
	}
}

void MCPDACClass::update() {
	// if (!this->action || this->ldac == false)
	// 	return;
	digitalWrite(this->ldacpin, LOW);
	delayMicroseconds(10);
	digitalWrite(this->ldacpin, HIGH);
	delayMicroseconds(10);
}

void MCPDACClass::updateRegister(bool chan)
{
	uint16_t command = 0;
	command |= (chan << REGAB);				   // set channel in register
	command |= (!this->gain[chan] << REGGA);   // set gain in register
	command |= (!this->shdn[chan] << REGSHDN); // set shutdown in register
	command |= (this->value[chan] & 0x0FFF);   // set input data bits (strip everything greater than 12 bit)

	digitalWrite(this->cspin, LOW);
	delayMicroseconds(10);
	shiftOut(sdi, sclk, MSBFIRST, command >> 8);
	shiftOut(sdi, sclk, MSBFIRST, command & 0xFF);
	delayMicroseconds(10);
	digitalWrite(this->cspin, HIGH);
}
void MCPDACx4::begin(uint8_t cspin)
{
	this->ldac = false;
	this->cspin = cspin;
	pinMode(this->cspin, OUTPUT);
	digitalWrite(this->cspin, HIGH);
	pinMode(sclk, OUTPUT);
	delayMicroseconds(10);
	pinMode(sdi[0], OUTPUT);
	pinMode(sdi[1], OUTPUT);
	pinMode(sdi[2], OUTPUT);
	pinMode(sdi[3], OUTPUT);
	delayMicroseconds(10);
}
void MCPDACx4::begin(uint8_t cspin, uint8_t sclk, uint8_t sdi0, uint8_t sdi1, uint8_t sdi2, uint8_t sdi3, uint8_t ldacpin)
{
	this->ldacpin = ldacpin;
	this->sclk = sclk;
	this->sdi[0] = sdi0;
	this->sdi[1] = sdi1;
	this->sdi[2] = sdi2;
	this->sdi[3] = sdi3;
	this->begin(cspin);
	this->ldac = true;
	pinMode(this->ldacpin, OUTPUT);
	digitalWrite(this->ldacpin, HIGH);
}
void MCPDACx4::updateRegister()
{
	uint16_t command[8];
	for (int i = 0; i < 8; i++)
	{
		command[i] = ((i & 1) << REGAB);		   // set channel in register
		command[i] |= (!this->gain[0] << REGGA);   // set gain in register
		command[i] |= (!this->shdn[0] << REGSHDN); // set shutdown in register
		command[i] |= (this->value[i] & 0x0FFF);   // set input data bits (strip everything greater than 12 bit)
	}
	digitalWrite(this->cspin, LOW);
	delayMicroseconds(10);
	for (int i = 0; i < 16; i++)
	{
		digitalWrite(this->sdi[0], command[0] & 0x8000);
		digitalWrite(this->sdi[1], command[2] & 0x8000);
		digitalWrite(this->sdi[2], command[4] & 0x8000);
		digitalWrite(this->sdi[3], command[6] & 0x8000);
		digitalWrite(this->sclk, HIGH);
		delayMicroseconds(10);
		digitalWrite(this->sclk, LOW);
		delayMicroseconds(10);
		command[0] <<= 1;
		command[2] <<= 1;
		command[4] <<= 1;
		command[6] <<= 1;
	}
	digitalWrite(this->cspin, HIGH);
	delayMicroseconds(10);
	digitalWrite(this->cspin, LOW);
	delayMicroseconds(10);
	for (int i = 0; i < 16; i++)
	{
		digitalWrite(this->sdi[0], command[1] & 0x8000);
		digitalWrite(this->sdi[1], command[3] & 0x8000);
		digitalWrite(this->sdi[2], command[5] & 0x8000);
		digitalWrite(this->sdi[3], command[7] & 0x8000);
		digitalWrite(this->sclk, HIGH);
		delayMicroseconds(10);
		digitalWrite(this->sclk, LOW);
		delayMicroseconds(10);
		command[1] <<= 1;
		command[3] <<= 1;
		command[5] <<= 1;
		command[7] <<= 1;
	}
	delayMicroseconds(10);
	digitalWrite(this->cspin, HIGH);
}
void MCPDACx4::setVoltage(uint8_t chan, uint16_t mv)
{
	// if (this->value[chan] != mv)
	{
		this->value[chan] = mv;
		// this->updateRegister(chan);
		this->action = true;
	}
}
void MCPDACx4::update()
{
	if (!this->action || this->ldac == false)
		return;
	updateRegister();
	digitalWrite(this->ldacpin, LOW);
	delayMicroseconds(10);
	digitalWrite(this->ldacpin, HIGH);
	delayMicroseconds(10);
}
